package reflects

import (
	"ecms/utils/converter"
	"errors"
	"fmt"
	"reflect"
	"strconv"
)

type Object struct {
	RefType      reflect.Type   `json:"ref_type"`       // 反射类型实例
	RefValue     reflect.Value  `json:"ref_value"`      // 反射对象实例
	JsonFieldMap map[string]int `json:"json_field_map"` // 改善字段查找效率
}

// NewObject 创建一个新的反射对象
func NewObject(target interface{}) (*Object, error) {
	refType := reflect.TypeOf(target)
	if refType.Kind() == reflect.Ptr {
		if refType.Elem().Kind() == reflect.Struct {
			refType = refType.Elem()
		} else {
			return nil, errors.New("target 必须为结构体指针, 不允许多级指针")
		}
	}
	refValue := reflect.ValueOf(target)
	if refValue.Kind() == reflect.Ptr {
		if refValue.Elem().Kind() == reflect.Struct {
			refValue = refValue.Elem()
		} else {
			return nil, errors.New("target 必须为结构体指针, 不允许多级指针")
		}
	}

	// 生成字段快速检索缓存
	jsonFieldMap := make(map[string]int)
	for i := 0; i < refType.NumField(); i++ {
		fieldType := refType.Field(i)
		if tmpField := fieldType.Tag.Get("json"); tmpField != "" {
			jsonFieldMap[tmpField] = i
		}
	}

	return &Object{
		RefType:      refType,
		RefValue:     refValue,
		JsonFieldMap: jsonFieldMap,
	}, nil
}

// SetValueByString 根据给定的 field, 找到 json tag 匹配的字段并将 value 转换为该字段的类型, 然后给该字段赋值
func (o *Object) SetValueByString(field, value string) {
	fieldIndex, has := o.JsonFieldMap[field]
	if !has {
		return
	}

	if o.RefValue.Field(fieldIndex).CanAddr() {
		// 字段匹配成功, 检查目标类型
		switch o.RefType.Field(fieldIndex).Type.Kind() {
		case reflect.String:
			o.RefValue.Field(fieldIndex).SetString(value)
		case reflect.Int, reflect.Int64:
			o.RefValue.Field(fieldIndex).SetInt(converter.Str2Int64(value))
		case reflect.Float64, reflect.Float32:
			o.RefValue.Field(fieldIndex).SetFloat(converter.Str2Float64(value))
		}
	}
}

// GenerateMap 根据当前的结构体导出生成 map
func (o *Object) GenerateMap() map[string]interface{} {
	result := make(map[string]interface{})
	for field, fieldIndex := range o.JsonFieldMap {
		if o.RefValue.Field(fieldIndex).CanAddr() {
			// 字段匹配成功, 检查目标类型
			switch o.RefType.Field(fieldIndex).Type.Kind() {
			case reflect.String:
				result[field] = o.RefValue.Field(fieldIndex).String()
			case reflect.Int, reflect.Int64:
				result[field] = o.RefValue.Field(fieldIndex).Int()
			case reflect.Float64, reflect.Float32:
				result[field] = o.RefValue.Field(fieldIndex).Float()
			}
		}
	}
	return result
}

// GenerateStringMap 根据当前的结构体导出生成 value 为 string 的 map
func (o *Object) GenerateStringMap() map[string]string {
	result := make(map[string]string)
	for field, fieldIndex := range o.JsonFieldMap {
		if o.RefValue.Field(fieldIndex).CanAddr() {
			// 字段匹配成功, 检查目标类型
			switch o.RefType.Field(fieldIndex).Type.Kind() {
			case reflect.String:
				result[field] = o.RefValue.Field(fieldIndex).String()
			case reflect.Int, reflect.Int64:
				result[field] = strconv.Itoa(int(o.RefValue.Field(fieldIndex).Int()))
			case reflect.Float64, reflect.Float32:
				result[field] = fmt.Sprintf("%.2f", o.RefValue.Field(fieldIndex).Float())
			}
		}
	}
	return result
}

// BatchSetValueFromFieldsMap 根据给定的 fields, 来批量设置在 map value 中对应值
func (o *Object) BatchSetValueFromFieldsMap(fields []string, value map[string]string) {
	for _, field := range fields {
		val, has := value[field]
		if has {
			o.SetValueByString(field, val)
		}
	}
}

// BatchSetValueFromFields 根据给定的 fields, 来批量设置在 interface{} value 中对应值
func (o *Object) BatchSetValueFromFields(fields []string, value interface{}) {
	refObj, err := NewObject(value)
	if err != nil {
		return
	}
	saveData := refObj.GenerateStringMap()

	o.BatchSetValueFromFieldsMap(fields, saveData)
}
